package com.py.framework.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.py.framework.common.Constants;
import com.py.framework.core.exception.ServiceException;
import com.py.framework.core.log.DebugLogger;
import com.py.framework.core.log.ExceptionLogger;
import com.py.framework.core.log.SystemLogger;
import com.py.framework.dao.SysLogDao;
import com.py.framework.entity.LogEntity;
import com.py.framework.service.SysLogService;

/**
 * 系统日志Service实现
 *
 * @author Leegern
 * @date 2018年4月22日
 */
@Service
public class SysLogServiceImpl implements SysLogService, Runnable {

    @Autowired
    private SysLogDao sysLogDao;

    /**
     * 执行数据写入的线程
     */
    private Thread thread;

    /**
     * 缓冲队列
     */
    private ArrayBlockingQueue<LogEntity> taskQueue;

    /**
     * 队列满的警报约束控制变量，最后警报时间
     */
    private long queueFullLastAlert = 0L;


    /*
     * (non-Javadoc)
     * @see com.py.framework.service.SysLogService#queryLogForPage(java.util.Map)
     */
    @Override
    public List<LogEntity> queryLogForPage(Map<String, Object> params) {
        return sysLogDao.queryLogForPage(params);
    }

    /*
     * (non-Javadoc)
     * @see com.py.framework.service.SysLogService#findOneLog(long)
     */
    @Override
    public LogEntity findOneLog(long id) {
        if (id > Constants.NUMBER_ZERO) {
            List<LogEntity> datas = sysLogDao.findOneLog(id);
            if (!CollectionUtils.isEmpty(datas)) {
                return datas.get(Constants.NUMBER_ZERO);
            }
        }
        return null;
    }

    /**
     * 日志批量入库
     *
     * @param datas
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int batchAddLogInfo(List<LogEntity> datas) {
        if (!CollectionUtils.isEmpty(datas)) {
            return sysLogDao.batchAddLogInfo(datas);
        }
        return 0;
    }

    /**
     * 开始线程
     */
    @PostConstruct
    public void init() {
        this.thread = new Thread(this);
        thread.start();
    }

    /**
     * 销毁bean时结束线程
     */
    @PreDestroy
    public void destroy() {
        thread.interrupt();
        try {
            thread.join(5000);
        } catch (InterruptedException e) {
            ExceptionLogger.error(e);
        }
    }

    /**
     * 系统日志放入缓冲队列
     *
     * @param entity 日志实例
     */
    @Override
    public void sendToQueue(LogEntity entity) {
        if (thread == null || thread.isInterrupted()) {
            throw new ServiceException("处理线程已终止，停止接收消息.");
        }
        try {
            boolean success = taskQueue.offer(entity, Constants.OFFER_WAITING_TIME, TimeUnit.MILLISECONDS);
            // 警报提醒队列已满
            if (!success) {
                long currentTime = System.currentTimeMillis();
                if (currentTime - queueFullLastAlert > Constants.QUEUE_FULL_ALERT_INTERVAL) {
                    SystemLogger.info("custom log to db, task queue full.");
                    queueFullLastAlert = currentTime;
                }
            }
        } catch (InterruptedException e) {
            ExceptionLogger.error("custom log to db, thread interrupted.", e.getMessage());
        }
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        SystemLogger.info("custom log to db thread started.");

        if (taskQueue == null) {
            taskQueue = new ArrayBlockingQueue<LogEntity>(Constants.DEFAULT_QUEUE_SIZE);
        }

        // 如果未终止线程，或者队列不为空
        List<LogEntity> batchItems = null;
        while (!Thread.currentThread().isInterrupted() || !taskQueue.isEmpty()) {
            batchItems = new ArrayList<LogEntity>(Constants.DEFAULT_BATCH_SIZE);
            if (!Thread.currentThread().isInterrupted()) {
                fillBatchItems(batchItems);
            } else {
                batchItems.addAll(taskQueue);
            }
            // 队列数据入MySQL库
            try {
                DebugLogger.debug("save log to db in buffered queue, the size is [" + batchItems.size() + "].");
                batchAddLogInfo(batchItems);
            } catch (Exception ex) {
                ExceptionLogger.error("queue into mysql error.", ex.getMessage());
            }
        }
        SystemLogger.info("custom log to  db thread ended.");
    }

    /**
     * 填充batch任务项目
     *
     * @param batchItems 数据集合
     */
    private void fillBatchItems(List<LogEntity> batchItems) {
        // 获取固定条数记录
        while (batchItems.size() < Constants.DEFAULT_BATCH_SIZE) {
            boolean gonext = false;
            LogEntity item;
            try {
                item = taskQueue.poll(Constants.POLL_WAITING_TIME, TimeUnit.MILLISECONDS);
                // 如果一定时间内还未取得数据并且队列中已有数据，则中断，把队列中的数据保存到数据库。
                if (item != null) {
                    batchItems.add(item);
                } else if (!batchItems.isEmpty()) {
                    gonext = true;
                }
            } catch (InterruptedException ex) {
                gonext = true;
            }
            if (gonext) {
                break;
            }
        }
    }

    
    @Override
	public List<Map> getLoginTimesByDept() {
		// TODO Auto-generated method stub
		return sysLogDao.getLoginTimesByDept();
	}

	@Override
	public List<LogEntity> queryLog(String realName, int logType) {
		// TODO Auto-generated method stub
		return sysLogDao.queryLog(realName,logType);
	}

}